﻿using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;
using MapDownloader.Core.MapProviders;
using MapDownloader.Core.Models;

namespace MapDownloader.Core.Core
{

   public class Maps : Singleton<Maps>
   {
      /// <summary>
      ///    是否存储到数据库
      /// </summary>
      public bool UseSaveToDb = false;
      /// <summary>
      /// 是否从服务器下载
      /// </summary>
      public bool UserProvider = false;

      /// <summary>
      ///    地图提供商，默认是高德
      /// </summary>
      public  BaseMapProvider Provider { get; set; }
      /// <summary>
      /// 瓦片的类型
      /// </summary>
      public TileTypeEnum TileType = TileTypeEnum.Base;
      /// <summary>
      /// 待存储瓦片队列
      /// </summary>
      private static readonly ConcurrentQueue<StorageQueueItem> StorageQueue = new ConcurrentQueue<StorageQueueItem>();
      /// <summary>
      /// 存储线程
      /// </summary>
      private static int _storeWorkers = 0;

      public Maps()
      {
         if (Instance != null)
            throw new Exception(
               "单例模式不能使用 new 实例化对象");
      }

      /// <summary>
      ///    获取一个瓦片
      /// </summary>
      /// <param name="pos">瓦片坐标</param>
      /// <param name="zoom">层级数量</param>
      /// <returns></returns>
      public BaseImage GetImageFrom(TilePoint pos)
      {
         BaseImage result = GetFromDataBase(pos.X, pos.Y, pos.Z, (int)TileType);
         if (result == null)
         {
            if (UserProvider)
            {
               try
               {
                  result = Provider.GetTileImage(pos);
                  Console.BackgroundColor = ConsoleColor.Green;
                  Console.WriteLine($"从源获取{pos}");
                  Console.ResetColor();
                  if (UseSaveToDb)
                  {
                     //StorageQueue.Enqueue(new StorageQueueItem(pos.X, pos.Y, zoom));
                     return StoreTitle(pos, result) ? result : null;
                  }
               }
               catch (Exception e)
               {
                  Console.BackgroundColor = ConsoleColor.Red;
                  Console.WriteLine(e);
                  Console.ResetColor();
               }
               
            }
         }
         return result;
      }


      /// <summary>
      ///    从数据库获取瓦片
      /// </summary>
      /// <param name="x">瓦片X坐标</param>
      /// <param name="y">瓦片Y坐标</param>
      /// <param name="zoom">层数</param>
      /// <returns></returns>
      public BaseImage GetImageFrom(long x, long y, int zoom)
      {
         return GetImageFrom(new TilePoint(x, y,zoom));
      }

      /// <summary>
      ///    从数据库中获取瓦片
      /// </summary>
      /// <param name="x">瓦片X坐标</param>
      /// <param name="y">瓦片Y坐标</param>
      /// <param name="zoom">层数</param>
      /// <param name="type"></param>
      /// <returns></returns>
      private BaseImage GetFromDataBase(long x, long y, int zoom,int type)
      {
         return Provider.Storager.GetTileFromStore(x, y, zoom,type);
      }
      /// <summary>
      /// 逐层存储瓦片
      /// </summary>
      /// <param name="zoom">开始层级</param>
      /// <param name="region">行政区域</param>
      public  void AutoStoreTile(int zoom,string region)
      {

         Provider.MinZoom = zoom;
         var storageQueueList = Provider.GetTilePointItem(region);
         foreach (var item in storageQueueList)
         {
            StorageQueue.Enqueue(item);
         }
         Console.WriteLine("计算完成，开始存储");
         StoreEngine();//启动存储
      }
      /// <summary>
      /// 逐层存储瓦片
      /// </summary>
      /// <param name="minLat">最小经度</param>
      /// <param name="minLng"></param>
      /// <param name="maxLat"></param>
      /// <param name="maxLng"></param>
      public void AutoStoreTile(double minLat,double minLng,double maxLat,double maxLng)
      {
         var storageQueueList = Provider.GetTilePointItem(new LatLngPoint(minLat,minLng),new LatLngPoint(maxLat,maxLng));
         foreach (var item in storageQueueList)
         {
            StorageQueue.Enqueue(item);
         }
         Console.WriteLine("计算完成，开始存储");
         StoreEngine();//启动存储

      }
      /// <summary>
      /// 存储引擎
      /// </summary>
      private void StoreEngine()
      {
         var processorCount = Environment.ProcessorCount;
         //TODO 当前有逻辑处理器
         while (Interlocked.Increment(ref _storeWorkers) < processorCount + 1)
         {
            Console.WriteLine($"线程:{_storeWorkers}开始工作");
            Task.Factory.StartNew(StoreWorker);
         }
         //var a= Task.Factory.StartNew(StoreWorker);

      }
      /// <summary>
      /// 获取待处理的瓦片
      /// </summary>
      /// <returns></returns>
      public long GetStorageQueueItemTodo()
      {
         return StorageQueue.Count;
      }
      /// <summary>
      /// 存储线程
      /// </summary>
      private  void StoreWorker()
      {
         
         while (StorageQueue.TryDequeue(out var tem))
         {
            try
            {
               if (!StoreTitle(new TilePoint(tem.X, tem.Y,tem.Zoom)))
                  Console.WriteLine($"{tem}存储失败");//TODO 记录日志
            }
            catch (Exception e)
            {
               Console.WriteLine($"{tem}存储失败，错误详情:{e.Message}");//TODO 记录日志
            }
         }
         Interlocked.Decrement(ref _storeWorkers);
      }
      /// <summary>
      /// 存储瓦片
      /// </summary>
      /// <param name="pos"></param>
      /// <returns></returns>
      private bool StoreTitle(TilePoint pos)
      {
         try
         {
            Thread.Sleep(Stuff.random.Next(0, 500));
            var result = GetFromDataBase(pos.X, pos.Y, pos.Z,(int)TileType);
            if (result == null)
            {
               result = Provider.GetTileImage(pos);
               return StoreTitle(pos,result);
            }
            return true;
         }
         catch (Exception ex)
         {
            Console.WriteLine("GetImageFrom: " + ex);
            throw;
         }
      }
      private bool StoreTitle(TilePoint pos, BaseImage img)
      {
            return Provider.Storager.PutTileToStore((int)TileType, pos, img.Data.GetBuffer());
      }
      
   }
}